package com.zwps.extra.http;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.lang.ParameterizedTypeImpl;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.lang.func.VoidFunc0;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.zwps.extra.http.response.message.DefaultResponseDataPageMessage;
import com.zwps.extra.http.response.message.InterfaceResponseDataMessage;
import com.zwps.extra.http.response.message.InterfaceResponseMessage;
import com.zwps.extra.http.response.model.vo.AbstractPageInfoVO;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;


@AllArgsConstructor
@Slf4j
public class HttpService {

    protected ObjectMapper objectMapper;

    protected Integer timeout = 15000;

    private RequestLogRecord record;

    public HttpService(RequestLogRecord record) {
        initObjectMapper();
        this.record = record;
    }

    public HttpService() {
        initObjectMapper();
    }

    private void initObjectMapper() {
        objectMapper = new ObjectMapper();
        // LocalDate、LocalDateTime序列化
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));

        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN);
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));

        objectMapper.registerModule(javaTimeModule);
        // 时间格式化
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN));
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
    }

    protected boolean success(int httpStatus) {
        return httpStatus == 200;
    }

    protected void setRequestBody(HttpRequest request, Object dto) throws HttpException {
        try {
            request.body(objectMapper.writeValueAsString(dto));
        } catch (JsonProcessingException e) {
            throw new HttpException("请求参数错误");
        }
    }
    
    protected Class<? extends InterfaceResponseMessage<?>> getInterfaceResponseMessageClass() {
        return null;
    }

    protected Class<? extends InterfaceResponseDataMessage<?, ?>> getInterfaceResponseDataMessageClass() {
        return null;
    }

    private LogAPIMessage.LogAPIMessageBuilder requestBefore(HttpRequest request) {
        LogAPIMessage.LogAPIMessageBuilder builder = null;
        if (record != null) {
            builder = LogAPIMessage.builder();
            builder.requestId(UUID.fastUUID().toString(true));
            builder.requestUrl(request.getUrl());
            builder.requestMethod(request.getMethod().name());
            builder.requestTime(System.currentTimeMillis());
            byte[] bodyBytes = request.bodyBytes();
            if (bodyBytes != null && bodyBytes.length > 0) {
                builder.requestBody(new String(bodyBytes));
            }
            builder.requestContentType(request.header(Header.CONTENT_TYPE));
            builder.logType(2);
            builder.operateType(0);
            builder.external(1);
        }
        return builder;
    }

    private LogAPIMessage.LogAPIMessageBuilder requestAfter(LogAPIMessage.LogAPIMessageBuilder builder,
            HttpResponse response, String body) {
        if (builder != null) {
            builder.httpStatus(response.getStatus());
            builder.responseBody(body);
            builder.responseContentType(response.header(Header.CONTENT_TYPE));
            builder.responseTime(System.currentTimeMillis());
            builder.operateStatus(0);
        }
        return builder;
    }

    protected <T> T request(HttpRequest request, TypeReference<T> type) throws Exception {
        if (timeout != null) {
            request.timeout(timeout);
        }
        LogAPIMessage.LogAPIMessageBuilder builder = requestBefore(request);
        try (HttpResponse response = request.execute()) {
            String body = response.body();
            builder = requestAfter(builder, response, body);
            if (!success(response.getStatus())) {
                log.error("request url [{}] error，response code [{}]", request.getUrl(), response.getStatus());
                System.out.println(response.getStatus());
                throw new HttpException("请求失败，状态码不符合预期", response.getStatus());
            }
            log.debug("request url [{}]，response body [{}]", request.getUrl(), body);
            T t = objectMapper.readValue(body, type);
            if (builder != null && t instanceof InterfaceResponseMessage) {
                InterfaceResponseMessage<?> message = (InterfaceResponseMessage<?>) t;
                if (!message.isSuccess()) {
                    builder.operateStatus(1);
                    builder.exceptionDetail(message.getMessage());
                }
            }
            return t;
        } catch (Exception e) {
            if (builder != null) {
                builder.operateStatus(1);
                builder.exceptionDetail(getStackTrace(e));
            }
            log.error("Request url[{}], Unexpected error in ", request.getUrl(), e);
            throw e;
        } finally {
            try {
                if (record != null) {
                    LogAPIMessage message = builder.build();
                    message.setCostTime(message.getResponseTime() - message.getRequestTime());
                    record.record(message);
                }
            } catch (Exception e2) {
                log.error("Record error, request url[{}], Unexpected error in ", request.getUrl(), e2);
            }
        }
    }

    private String getStackTrace(final Throwable throwable) {
        final StringWriter sw = new StringWriter();
        final PrintWriter pw = new PrintWriter(sw, true);
        throwable.printStackTrace(pw);
        return sw.getBuffer().toString();
    }

    protected <T extends InterfaceResponseMessage<?>> T requestSuccessed(HttpRequest request, TypeReference<T> type)
            throws Exception {
        T message = request(request, type);
        if (!message.isSuccess()) {
            throw new HttpException(message.getMessage());
        }
        return message;
    }

    /**
     * 处理
     * 
     * @Title: requestSuccessed
     * @Description:
     * @param <T>
     * @param request
     * @param classz              反序列化的对象类型
     * @param actualTypeArguments 实际的泛型参数类型
     * @return
     * @throws Exception
     * @author lyh
     * @date 2023年10月25日 下午2:11:26
     */
    @SuppressWarnings("rawtypes")
    protected <T extends InterfaceResponseMessage<?>> T requestSuccessed(HttpRequest request,
            Class<? extends InterfaceResponseMessage> classz, Type actualTypeArgument) throws Exception {
        T message = requestSuccessed(request, new TypeReference<T>() {
            @Override
            public Type getType() {
                return new ParameterizedTypeImpl(new Type[] { actualTypeArgument }, classz, classz);
            }
        });
        return message;
    }
    

    @SuppressWarnings("rawtypes")
    protected <T extends InterfaceResponseMessage<?>> T requestSuccessed(HttpRequest request,
             Class<? extends InterfaceResponseMessage> classz) throws Exception {
        T message = requestSuccessed(request, new TypeReference<T>() {
            @Override
            public Type getType() {
                return new ParameterizedTypeImpl(new Type[] { }, classz, classz);
            }
        });
        return message;
    }
    
    protected <T extends InterfaceResponseMessage<?>> T requestSuccessed(HttpRequest request) throws Exception {
        T message = requestSuccessed(request, new TypeReference<T>() {
            @Override
            public Type getType() {
                Class<? extends InterfaceResponseMessage<?>> classz = getInterfaceResponseMessageClass();
                return new ParameterizedTypeImpl(new Type[] {}, classz, classz);
            }
        });
        return message;
    }

    @SuppressWarnings("rawtypes")
    protected <T> T requestReturnData(HttpRequest request, Class<? extends InterfaceResponseDataMessage> classz,
            Type actualTypeArgument) throws Exception {
        InterfaceResponseDataMessage<?, T> data = requestSuccessed(request, classz, actualTypeArgument);
        return data.getData();
    }

    @SuppressWarnings("rawtypes")
    protected <T> T requestReturnData(HttpRequest request, Class<? extends InterfaceResponseDataMessage> classz)
            throws Exception {
        InterfaceResponseDataMessage<?, T> data = requestSuccessed(request, classz, null);
        return data.getData();
    }

    protected <T> T requestReturnData(HttpRequest request,
            TypeReference<? extends InterfaceResponseDataMessage<?, T>> type) throws Exception {
        InterfaceResponseDataMessage<?, T> data = requestSuccessed(request, type);
        return data.getData();
    }

    protected <T> AbstractPageInfoVO<T> requestReturnDataPage(HttpRequest request,
            Class<? extends InterfaceResponseDataMessage<?, T>> classz, Type actualTypeArgument) throws Exception {
        DefaultResponseDataPageMessage<T> data = requestSuccessed(request, classz, actualTypeArgument);
        return data.getData();
    }

    protected <T> AbstractPageInfoVO<T> requestReturnDataPage(HttpRequest request,
            Class<? extends InterfaceResponseDataMessage<?, T>> classz) throws Exception {
        DefaultResponseDataPageMessage<T> data = requestSuccessed(request, classz, null);
        return data.getData();
    }

    protected <T> AbstractPageInfoVO<T> requestReturnDataPage(HttpRequest request,
            TypeReference<? extends DefaultResponseDataPageMessage<T>> type) throws Exception {
        DefaultResponseDataPageMessage<T> data = requestSuccessed(request, type);
        return data.getData();
    }

    protected <T> T exceptionHandle(Func0<T> fun, String message) {
        try {
            return fun.call();
        } catch (HttpException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Unexpected error in ", e);
            throw new HttpException(message);
        }
    }
    
    protected void exceptionHandle(VoidFunc0 fun, String message) {
        try {
            fun.call();
        } catch (HttpException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Unexpected error in ", e);
            throw new HttpException(message);
        }
    }
}
